import 'package:flutter/cupertino.dart';
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

/// TCP 客户端，支持json和msgpack两种格式.
class SocketTCPClient {
  Socket? _socket;

  /// 服务器IP地址
  final String _host;

  /// 端口
  final int _port;

  /// 重连次数
  int? reconnectCount;

  /// 重连时间间隔
  int? reconnectInterval;

  /// 重连定时器
  bool socketOpened = false;

  List<int> buff = [];

  /// 服务器IP
  String get host => _host;

  /// 服务器端口
  int get port => _port;

  int _curReconn = 0;

  Timer? _timer;

  int _seq = 0;

  bool _closeBySelf = false;

  bool _isConn = false;

  bool get isConn => _isConn;

  /// 当前重连次数
  int get curReconnect => _curReconn;

  void Function(SocketTCPClient client, Map cmd)? onData;

  void Function(SocketTCPClient client)? onConnect;

  void Function(SocketTCPClient client)? onDone;

  SocketTCPClient(
    this._host,
    this._port, {
    required this.onConnect,
    required this.onData,
    this.onDone,
    this.reconnectCount,
    this.reconnectInterval,
  }) {
    reconnectCount ??= -1;
    reconnectInterval ??= 3000;
  }

  /// 连接服务器
  Future<void> connect() async {
    buff = [];
    debugPrint("开始连接...");
    await Socket.connect(host, port).then((Socket socket) {
      _socket = socket;
      _curReconn = 0;
      _closeBySelf = false;
      _isConn = true;

      socket.listen((data) {
        _onData(data);
      }, onError: (error, stack) {
        debugPrint(" socket error = $error");
        _isConn = false;
        var tmp = reconnectCount!.toUnsigned(32);
        if (_curReconn < tmp && !_closeBySelf) {
          onDone!(this);
        } else {
          _reConn();
        }
      }, onDone: () async {
        _isConn = false;
        if (!_closeBySelf) {
          var tmp = reconnectCount!.toUnsigned(32);
          if (_curReconn < tmp) {
            _reConn();
          } else {
            onDone!(this);
          }
        } else {
          onDone!(this);
        }
      });
      onConnect!(this);
    }).catchError((e) {
      _isConn = false;
      debugPrint(" socket error = $e");
      _reConn();
    });
  }

  /// 断开连接
  close() {
    _closeBySelf = true;
    _isConn = false;
    _socket?.close();
    _curReconn = 0;
    _socket?.destroy();
    // buff = [];
  }

  dispose() {
    _seq = _seq + 1;
    _socket?.close();
    _socket?.destroy();
  }

  void _reConn() async {
    int curSeq = _seq;
    Future.delayed(Duration(milliseconds: reconnectInterval!), () {
      var tmp = reconnectCount!.toUnsigned(32);
      if (curSeq == _seq && _curReconn < tmp && !_closeBySelf) {
        _socket = null;
        _curReconn += 1;
        debugPrint("正在进行断线重连 $_curReconn");
        connect();
      }
    });
  }

  _onData(Uint8List event) async {
    // debugPrint('----------> $event  type=${event.runtimeType}');
    buff += event;
    // debugPrint('当前的缓冲区长度为: ${buff.length}');

    while (buff.length > 2) {
      int packLen = 0;
      List<int> lenBuf = buff.sublist(0, 2);
      Uint8List tmp = Uint8List.fromList(lenBuf);
      ByteData tmpByteData = ByteData.sublistView(tmp);
      packLen = tmpByteData.getUint16(0);

      // debugPrint('长度: $lenBuf');
      // debugPrint('长度: $packLen');

      var totalLen = packLen + 2;
      // debugPrint('长度: $totalLen');

      if (buff.length >= totalLen) {
        // List<int> curFrame = buff.sublist(2, totalLen);
        List<int> packed = buff.sublist(2, 2 + packLen);

        // debugPrint("数据满足条件 ${buff.length}");
        buff.replaceRange(0, totalLen, []);

        // debugPrint("json解码数据长度 ${packed.length}");
        var decoded = utf8.decode(packed);
        Map cmd = jsonDecode(decoded);
        // debugPrint("json------------>  data= $cmd");
        onData!(this, cmd);

        // debugPrint("数据剩余长度 ${buff.length}");
      } else {
        // debugPrint("数据长度不足 ${buff.length}");
        break;
      }
    }
  }

  /// 发送数据
  send(Map cmd) {
    // var cmd = {'name':"login", 'data' : "aaa"};//{"uid":123, "roomid":"123456"}
    // debugPrint("<------------  data=$cmd");

    var bytes;

    // debugPrint("发送json");
    String s = jsonEncode(cmd);
    bytes = utf8.encode(s);
    var d = bytes.length;

    var l = d + 2;
    Uint8List byteList = Uint8List(2);
    ByteData byteData = ByteData.sublistView(byteList);
    byteData.setUint16(0, d);
    var message = Uint8List(l);
    var tmpData = ByteData.view(message.buffer);
    // tmpData.setUint8(0, 0x02);

    for (var i = 0; i < byteList.length; i++) {
      tmpData.setUint8(i, byteData.getUint8(i));
    }
    for (var i = 0; i < d; i++) {
      tmpData.setUint8(i + 2, bytes[i]);
    }
    // tmpData.setUint8(d + 5, 0x01);
    // tmpData.setUint8(d + 6, 0x02);
    // tmpData.setUint8(d + 7, 0x03);
    _socket!.add(message);
  }
}
